package com.eric.utils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Component
public class FilesDownLoad {
    /*方法说明：
     1、该方法是用于单文件下载方法的依赖；
     2、本方法接收两个参数，分别是fileName,fileLocation
            fileName：是前端传来需要下载的文件的全网路地址名称，如：http://localhost:80/file/2023/7/25/xxx.doc
            fileLocation:匹配springboot中yml文件中的file:Location:e:/code/,就是服务器上存放文件的那个目录e:/code/，用来解析
            fileName地址为服务器的地址，如fileName网络地址是：http://localhost:80/file/2023/7/25/xxx.doc，如果fileLocation地址
            为e:/code/，那么通过urlToLocationName解析后，返回的地址就是e:/code/2023/7/25/***.doc
     3、本方法返回一个字符串值，用于返回一个在服务器上已有文件的全文件名，如e:/code/2023/07/25/xxx.doc
     */
    public static String urlToLocalName(String fileName,String fileLocation){
        return fileLocation+fileName.substring(fileName.indexOf("e")+1);
    }
    /*
         把文件传送到前端被下载的公共方法，共两个参数：
         1、第一个localFileName，LocalFileName:通过urlToLocalName方法获取
         2、第二个参数response，硬编码，就是HttpServletResponse对象
         3、本函数是没有返回值
         3、这个函数可以随便复用，记住以上两个参数接口就好了，别的程序可以直接复用
         */
    public static void fileSend(String LocalFileName, HttpServletResponse response) throws IOException {
        File file=new File(LocalFileName);
        FileInputStream fs=new FileInputStream(file);
        InputStream fis=new BufferedInputStream(fs);
        byte[] buffer=new byte[fis.available()];
        fis.read(buffer);
        fis.close();
//        先清空response
        response.reset();
//        下面是固定编码
//        设置response的Header
        response.setCharacterEncoding("UTF-8");
//     Content-Disposition的作用：告知浏览器以何种方式显示响应返回的文件，用浏览器打开还是以附件的形式下载到本地保存
//     attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
//     filename表示文件的默认名称，因为网络传输只支持URL编码的相关支付，因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
//        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filelocalname, "UTF-8"));
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(LocalFileName, "UTF-8"));
//    告知浏览器文件的大小
        response.addHeader("Content-Length",""+file.length());
        OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
        response.setContentType("application/octet-stream");
        response.setHeader("Access-Control-Allow-Origin","*");
        outputStream.write(buffer);
        outputStream.flush();
        outputStream.close();
    }

    //    多文件下载方法
    /*接口文档：
     *1、string[]files:接收一个字符串数组，里面是传来的网络全文件名数组，如几个http://localhost:80/file/2023/7/25/xxx.doc相类似字符串
     * 记住，这个地址，必须有这个file这个文件夹，或者文件夹名称最后的字符必须是"e"，否则会在后面截取文件名是报错
     *2、fileLocation:传来的一个服务器上存放文件的地址，一般与application.yml中的file:Location: e:/code/匹配，如果调用的上下文中，有一个
     * @Value("file.location")，file.location对应的yml中的地址是e:/code/，那么这个e:/code/就是这fileLocation,这个主要是用来存放压缩包临时地址，
     * 实际上这个地址可以随便改，但考虑到服务器上盘符，或者操作系统不一致，一般建议使用yml文件中的那个file:Location表示的地址，以免出错。
     * 本方法没有返回值，但会把几个文件打包成一个zip文件并向前端发送
     * */
    public static void filesToZip(HttpServletResponse response, String[] files,String fileLocation) throws IOException {
        String zipName="tempFileZip.zip";
        int size = files.length;
        String strZipPath=fileLocation+zipName;
//        strZipPath：就是把多个文件打包成一个压缩包，这个压缩包在服务器上的全路径名，如strZipPath:e:/code/filesZip.zip，传送到前端后，该文件会被删除
        ZipOutputStream zipStream=null;
        FileInputStream zipSource=null;
        BufferedInputStream bufferStream=null;
        File zipFile=new File(strZipPath);
        try {
//            构造最终压缩包的输出流
            zipStream=new ZipOutputStream(Files.newOutputStream(zipFile.toPath()));
            for (int i = 0; i < size; i++) {
//                构成下面三行代码的前提，是全地址名一定是：http://*****:****/file/2023/7/25/xxx.doc
//                一定有这个file，好方便截取全文件名字符串
                int e= files[i].indexOf("e")+1;
                String realFilename=files[i].substring(files[i].lastIndexOf("/")+1);
                String realFilePath=fileLocation+files[i].substring(e);
                File file=new File(realFilePath);
                if (file.exists()) {
                    zipSource = new FileInputStream(file);//将需要压缩的文件格式化为输入流
                    ZipEntry zipEntry = new ZipEntry(realFilename);//在压缩目录中文件的名字
                    zipStream.putNextEntry(zipEntry);//定位该压缩条目位置，开始写入文件到压缩包中
                    bufferStream = new BufferedInputStream(zipSource, 1024 * 10);
                    int read = 0;
                    byte[] buf = new byte[1024 * 10];
                    while ((read = bufferStream.read(buf, 0, 1024 * 10)) != -1) {
                        zipStream.write(buf, 0, read);
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            //关闭流
            try{
                if(null != bufferStream) bufferStream.close();
                if(null != zipStream){
                    zipStream.flush();
                    zipStream.close();
                }
                if(null != zipSource) zipSource.close();
            }catch (IOException exception){
                exception.printStackTrace();
            }
        }
        if(zipFile.exists()){
//        文件输出模块，不详直接在浏览器中打开，就按下面方式打开
//        filelocalname是下载文件的本地路径,下面5行代码是把文件写入输入流
            File file=new File(strZipPath);
            FileInputStream fileInputStream = new FileInputStream(file);
            InputStream fis = new BufferedInputStream(fileInputStream);
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
//            压缩包生成后，用本类中另外一个fileSend方法传送文件到前端
//            里面有两个参数，分别是strZipPath，response，strZipPath是这个zip文件在服务器上的全路径，如e:/code/filesZip.zip
//            response:就是固定的
            fileSend(strZipPath,response);
//            删除生成的临时zip文件
//            zipFile.delete();
        }
    }
}
